#region Using directives
using SlimDX;
using SlimDX.Direct3D10;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Text;
using System.Xml;
#endregion

#if HD
namespace HDx.Core.Collada
#else
namespace ColladaLoader
#endif
 {
  public class ColladaModel : IDisposable {
    public const string ColladaExtension = "DAE";

    public string name;

    public Dictionary<string, string> images = new Dictionary<string, string>();
    public List<Material> materials = new List<Material>();
    public List<EffectNode> EffectNodes = new List<EffectNode>();

    public List<MeshContainer> meshes = new List<MeshContainer>();
    public List<SceneNode> SceneNodes = new List<SceneNode>();
    public List<Bone> bones = new List<Bone>(); //List of bones, first bone = root bone

    int numOfAnimations = 1; // Number of values in the animationMatrices in each bone.
    float frameRate = 30;

    #region Constructor
    public ColladaModel(string setName) {
      name = setName;
      string modelpath = "..\\..\\..\\..\\models\\";
      string filename = Path.ChangeExtension(name, ColladaExtension);

      using (Stream file = System.IO.File.OpenRead(modelpath+filename)) {
        string colladaXml = new StreamReader(file).ReadToEnd();
        XmlNode colladaFile = XmlHelper.LoadXmlFromText(colladaXml);

        LoadScene(colladaFile);
        LoadMaterial(colladaFile);
        LoadBones(colladaFile);
        CalculateAbsoluteBoneMatrices();
        LoadMesh(colladaFile);
        // Load controller node ???
        LoadAnimation(colladaFile);
      }
    } // ColladaModel(setFilename)
    #endregion

    #region Dispose
    public void Dispose() { }
    #endregion

    #region Matrix helper methods

    //This function "repairs" the world matrix to compensate for the fact that Blender uses a different axis system
    private Matrix FixMatrix(Matrix m) {

      Vector3 VTranslate;
      Vector3 VScale;
      Vector3 VRotate;
      Quaternion QRotate;

      //Decompose matrix into it's components
      m.Decompose(out VScale, out QRotate, out VTranslate);

      VRotate.X = QRotate.X;
      VRotate.Y = QRotate.Z; //swap components
      VRotate.Z = QRotate.Y;

      //return axis swapped matrix
      return Matrix.Scaling(VScale) * Matrix.RotationAxis(VRotate, -QRotate.Angle) * Matrix.Translation(VTranslate);
    }

    private static Matrix LoadColladaMatrix(float[] mat, int offset) {
      Matrix m = new Matrix();

      // Put array of floats into matrix.
      for (int i = 0; i <= 15; i++) {
        //m[i/4, i%4] = mat[i]; //Rowbased vs columnbased
        m[i % 4, i / 4] = mat[i];
      }
      return m;
    }
    #endregion

    #region Load materials, images and effects
    /// <summary>
    /// Load material information from a collada file
    /// </summary>
    /// <param name="colladaFile">Collada file xml node</param>
    private void LoadMaterial(XmlNode colladaFile) {
      System.Console.Out.WriteLine("*Begin Loadmaterial*");

      #region library-images
      //////////////////////////////////////
      ////Library-images
      //////////////////////////////////////

      // First get all textures, if there are none, ignore and quit.
      XmlNode texturesNode = XmlHelper.GetChildNode(colladaFile, "library_images");
      if (texturesNode == null)
        return;

      // Get all used texture images
      foreach (XmlNode textureNode in texturesNode) {
        if (textureNode.Name == "image") {
          string filename = StringHelper.ExtractFilename(XmlHelper.GetChildNode(textureNode, "init_from").InnerText, false);
          string imgname = XmlHelper.GetXmlAttribute(textureNode, "name");

          //Add to images dictionary if it's not allready in the dictionary, and the filename is filled in
          if (!(images.ContainsKey(imgname)) && (filename.Length != 0)) {
            images.Add(imgname, filename);
            System.Console.Out.WriteLine("Texture '" + imgname + "'found: " + filename);
          }
        } else
          throw new InvalidOperationException("Unknown Node " + textureNode.Name + " found in library_images");
      }
      #endregion

      #region library-effects
      //////////////////////////////////////
      ////Library-effects
      //////////////////////////////////////

      // Load all effects to find out the used effect in our material
      XmlNode effectsNode = XmlHelper.GetChildNode(colladaFile, "library_effects");
      if (effectsNode == null)
        throw new InvalidOperationException("library_effects node not found while loading Collada file " + name);

      foreach (XmlNode effectNode in effectsNode) {
        XmlNode effect = XmlHelper.GetChildNode(effectNode, "init_from");

        EffectNode e = new EffectNode();
        e.Name = XmlHelper.GetXmlAttribute(effectNode, "name");

        if (effect != null)
          e.InitFrom = effect.InnerText;

        //if there's a texture, there's no diffuse color
        XmlNode DiffuseNode = XmlHelper.GetChildNode(XmlHelper.GetChildNode(effectNode, "diffuse"), "color");
        if (DiffuseNode != null) {
          float[] Diffuse = StringHelper.ConvertStringToFloatArray(DiffuseNode.InnerText);
          e.FDiffuse = new Vector4(Diffuse[0], Diffuse[1], Diffuse[2], Diffuse[3]);
        }

        XmlNode AmbientNode = XmlHelper.GetChildNode(effectNode, "ambient");
        if (AmbientNode != null) {
          XmlNode ColorNode = XmlHelper.GetChildNode(AmbientNode, "color");
          float[] Ambient = StringHelper.ConvertStringToFloatArray(ColorNode.InnerText);
          e.FAmbient = new Vector4(Ambient[0], Ambient[1], Ambient[2], Ambient[3]);
        }

        XmlNode SpecularNode = XmlHelper.GetChildNode(effectNode, "specular");
        if (SpecularNode != null) {
          XmlNode ColorNode = XmlHelper.GetChildNode(SpecularNode, "color");
          float[] Specular = StringHelper.ConvertStringToFloatArray(ColorNode.InnerText);
          e.FSpecular = new Vector4(Specular[0], Specular[1], Specular[2], Specular[3]);
        }


        XmlNode ShininessNode = XmlHelper.GetChildNode(effectNode, "shininess");
        if (ShininessNode != null) {
          e.FShininess = Convert.ToSingle(XmlHelper.GetChildNode(ShininessNode, "float").InnerText);
        }

        XmlNode TransparencyNode = XmlHelper.GetChildNode(effectNode, "transparency");
        if (TransparencyNode != null) {
          e.FAlpha = Convert.ToSingle(XmlHelper.GetChildNode(TransparencyNode, "float").InnerText);
        }


        //Add to effects dictionary
        EffectNodes.Add(e);
      }
      #endregion

      #region Library-materials
      //////////////////////////////////////
      ////Library-Materials
      //////////////////////////////////////

      XmlNode materialsNode = XmlHelper.GetChildNode(colladaFile, "library_materials");
      if (materialsNode == null)
        throw new InvalidOperationException("library_materials node not found while loading Collada file " + name);

      foreach (XmlNode materialNode in materialsNode) {
        if (materialNode.Name == "material") {
          Material m = new Material();
          m.FName = XmlHelper.GetXmlAttribute(materialNode, "name");
          m.FEffect = XmlHelper.GetXmlAttribute(XmlHelper.GetChildNode(materialNode, "instance_effect"), "url");
          //Add the effect to the list
          materials.Add(m);
        } else
          throw new InvalidOperationException("Unknown Node " + materialNode.Name + " found in library_materials");
      }
      #endregion

      System.Console.Out.WriteLine("*End Loadmaterial*");
    }
    #endregion

    #region Load bones
    private void LoadBones(XmlNode colladaFile) {
      // We need to find the bones in the visual scene
      XmlNode visualSceneNode = XmlHelper.GetChildNode(XmlHelper.GetChildNode(colladaFile, "library_visual_scenes"), "visual_scene");

      FillBoneNodes(null, visualSceneNode);
    }

    private void FillBoneNodes(Bone parentBone, XmlNode boneNodes) {
      foreach (XmlNode boneNode in boneNodes)
        if (boneNode.Name == "node" && (XmlHelper.GetXmlAttribute(boneNode, "id").Contains("Bone") || XmlHelper.GetXmlAttribute(boneNode, "type").Contains("JOINT"))) {
          Matrix matrix = Matrix.Identity;

          // Get all sub nodes for the matrix
          foreach (XmlNode subnode in boneNode) {
            switch (subnode.Name) {
              case "translate": System.Console.Out.WriteLine("translate not implemented yet for bone" + bones.Count); break;
              case "rotate": System.Console.Out.WriteLine("rotate not implemented yet for bone" + bones.Count); break;
              case "matrix": matrix = FixMatrix(LoadColladaMatrix(StringHelper.ConvertStringToFloatArray(subnode.InnerText), 0));
                break;
            }
          }

          // Create this node, use the current number of bones as number.
          Bone newBone = new Bone(matrix, parentBone, bones.Count, XmlHelper.GetXmlAttribute(boneNode, "sid"));

          // Add to our global bones list
          bones.Add(newBone);
          // And to our parent, this way we have a tree and a flat list in
          // the bones list :)
          if (parentBone != null) parentBone.children.Add(newBone);

          // Create all children (will do nothing if there are no sub bones)
          FillBoneNodes(newBone, boneNode);
        }
    }
    #endregion

    #region Load Scene

    private void LoadScene(XmlNode colladaFile) {

      XmlNode scenes = XmlHelper.GetChildNode(colladaFile, "library_visual_scenes");
      if (scenes == null)
        throw new InvalidOperationException("library_visual_scenes node not found in collada file " + name);

      foreach (XmlNode scene in scenes) {
        if (scene.Name == "visual_scene") {

          foreach (XmlNode objectnode in scene) {
            SceneNode sceneNode = new SceneNode();
            sceneNode.Name = XmlHelper.GetXmlAttribute(objectnode, "name");

            foreach (XmlNode subnode in objectnode) {

              switch (subnode.Name) {
                case "instance_geometry":
                  sceneNode.Type = SceneNode.SceneNodeType.Geometry;
                  sceneNode.GeometryLink = XmlHelper.GetXmlAttribute(subnode, "url").Replace("#", "");

                  XmlNode BindMaterials = XmlHelper.GetChildNode(subnode, "bind_material");
                  XmlNode Techniques = XmlHelper.GetChildNode(BindMaterials, "technique_common"); // if this line fails, your modellers forgot to texture a mesh !

                  foreach (XmlNode instancematerial in Techniques) {
                    if (instancematerial.Name != "instance_material")
                      break;
                    sceneNode.Materials.Add(XmlHelper.GetXmlAttribute(instancematerial, "symbol"));
                  }
                  Console.WriteLine("instance geometry");
                  break;
                case "instance_camera":
                  sceneNode.Type = SceneNode.SceneNodeType.Camera;
                  Console.WriteLine("instance camere");
                  //make camera entry
                  break;
                case "instance_light":
                  sceneNode.Type = SceneNode.SceneNodeType.Light;
                  Console.WriteLine("instance light");
                  //make light entry
                  break;
                case "matrix":
                  sceneNode.World = FixMatrix(LoadColladaMatrix(StringHelper.ConvertStringToFloatArray(subnode.InnerText), 0));
                  // save position matrix for object
                  break;

                default:
                  Console.WriteLine("Unknow node found, problem !");
                  break;
              }

            }
            SceneNodes.Add(sceneNode);
          }
        }
      }
    }

    #endregion

    #region Load mesh
    private void LoadMesh(XmlNode colladaFile) {
      XmlNode geometrys = XmlHelper.GetChildNode(colladaFile, "library_geometries");
      if (geometrys == null)
        throw new InvalidOperationException("library_geometries node not found in collada file " + name);

      foreach (XmlNode geometry in geometrys) {
        System.Console.WriteLine(geometry.Name);

        if (geometry.Name == "geometry") {
          //Enumerate mesh nodes
          XmlNode meshnode = XmlHelper.GetChildNode(geometry, "mesh");

          // Load everything from the mesh node
          List<MeshContainer> newmeshes = LoadMeshGeometry(colladaFile, meshnode, XmlHelper.GetXmlAttribute(geometry, "id"));
          foreach (MeshContainer m in newmeshes) {
            //GenerateVertexAndIndexBuffers(meshes[meshes.Count - 1]);
            m.GenerateVertexAndIndexBuffers();
            meshes.Add(m);
          }

          // Generate vertex buffer for current mesh node

        }
      }
    }

    /// <summary>
    /// Load mesh geometry
    /// </summary>
    /// <param name="geometry"></param>
    private List<MeshContainer> LoadMeshGeometry(XmlNode colladaFile, XmlNode meshNode, String name) {
      List<MeshContainer> newmeshes = new List<MeshContainer>();


      #region Load all source nodes
      Dictionary<string, List<float>> sources = new Dictionary<string, List<float>>();
      foreach (XmlNode node in meshNode) {
        if (node.Name != "source")
          continue;
        XmlNode floatArray = XmlHelper.GetChildNode(node, "float_array");
        List<float> floats = new List<float>(StringHelper.ConvertStringToFloatArray(floatArray.InnerText));

        // Fill the array up
        int count = Convert.ToInt32(XmlHelper.GetXmlAttribute(floatArray, "count"), NumberFormatInfo.InvariantInfo);
        while (floats.Count < count)
          floats.Add(0.0f);

        sources.Add(XmlHelper.GetXmlAttribute(node, "id"), floats);
      } // foreach
      #endregion

      #region Vertices
      // Also add vertices node, redirected to position node into sources
      XmlNode verticesNode = XmlHelper.GetChildNode(meshNode, "vertices");
      XmlNode posInput = XmlHelper.GetChildNode(verticesNode, "input");
      if (XmlHelper.GetXmlAttribute(posInput, "semantic").ToLower(CultureInfo.InvariantCulture) != "position")
        throw new InvalidOperationException("unsupported feature found in collada \"vertices\" node");
      string verticesValueName = XmlHelper.GetXmlAttribute(posInput, "source").Substring(1);
      sources.Add(XmlHelper.GetXmlAttribute(verticesNode, "id"), sources[verticesValueName]);
      #endregion

      #region Skincontroller
      /*
      XmlNode skincontroller = XmlHelper.GetChildNode(colladaFile, "library_controllers");
      List<Vector3> vertexSkinJoints = new List<Vector3>();
      List<Vector3> vertexSkinWeights = new List<Vector3>();

      if (skincontroller == null) {
        System.Console.Out.WriteLine("The model has no skinning data or there was an error finding it");
      }

      else {
        System.Console.Out.WriteLine("Loading skinning data");
        

        #region Load helper matrixes (bind shape and all bind poses matrices)
        // Fixme: multiple skinns: We only support 1 skinning, so just load the first skin node!

        XmlNode skinNode = XmlHelper.GetChildNode(XmlHelper.GetChildNode(XmlHelper.GetChildNode(colladaFile, "library_controllers"), "controller"), "skin");
        m.objectMatrix = FixMatrix(LoadColladaMatrix(StringHelper.ConvertStringToFloatArray(XmlHelper.GetChildNode(skinNode, "bind_shape_matrix").InnerText), 0));

        // Get the order of the bones used in collada (can be different than ours)
        int[] boneArrayOrder = new int[bones.Count];
        int[] invBoneArrayOrder = new int[bones.Count];
        string boneNameArray = XmlHelper.GetChildNode(skinNode, "Name_array").InnerText;
        int arrayIndex = 0;
        foreach (string boneName in boneNameArray.Split(' ')) {
          boneArrayOrder[arrayIndex] = -1;
          foreach (Bone bone in bones)
            if (bone.id == boneName) {
              boneArrayOrder[arrayIndex] = bone.num;
              invBoneArrayOrder[bone.num] = arrayIndex;
              break;
            } // foreach

          if (boneArrayOrder[arrayIndex] == -1)
            throw new InvalidOperationException(
              "Unable to find boneName=" + boneName +
              " in our bones array for skinning!");
          arrayIndex++;
        } // foreach
        #endregion

        #region Load weights
        float[] weights = null;
        foreach (XmlNode sourceNode in skinNode) {
          // Get all inv bone skin matrices
          if (sourceNode.Name == "source" &&
            XmlHelper.GetXmlAttribute(sourceNode, "id").Contains("bind_poses")) {
            // Get inner float array
            float[] mat = StringHelper.ConvertStringToFloatArray(
              XmlHelper.GetChildNode(sourceNode, "float_array").InnerText);
            for (int boneNum = 0; boneNum < bones.Count; boneNum++)
              if (mat.Length / 16 > boneNum) {
                bones[boneArrayOrder[boneNum]].invBoneSkinMatrix = FixMatrix(LoadColladaMatrix(mat, boneNum * 16));
              } // for if
          } // if

          // Get all weights
          if (sourceNode.Name == "source" && XmlHelper.GetXmlAttribute(sourceNode, "id").Contains("skin-weights")) {
            // Get inner float array
            weights = StringHelper.ConvertStringToFloatArray(XmlHelper.GetChildNode(sourceNode, "float_array").InnerText);
          } // if
        } // foreach

        if (weights == null)
          throw new InvalidOperationException("No weights were found in our skin, unable to continue!");
        #endregion

        #region Prepare weights and joint indices, we only want the top 3!
        // Helper to access the bones (first index) and weights (second index).
        // If we got more than 2 indices for an entry here, there are multiple
        // weights used (usually 1 to 3, if more, we only use the strongest).
        XmlNode vertexWeightsNode = XmlHelper.GetChildNode(skinNode, "vertex_weights");
        int[] vcountArray = StringHelper.ConvertStringToIntArray(XmlHelper.GetChildNode(skinNode, "vcount").InnerText);
        int[] vArray = StringHelper.ConvertStringToIntArray(XmlHelper.GetChildNode(skinNode, "v").InnerText);

        // Build vertexSkinJoints and vertexSkinWeights for easier access.
        int vArrayIndex = 0;
        for (int num = 0; num < vcountArray.Length; num++) {
          int vcount = vcountArray[num];
          List<int> jointIndices = new List<int>();
          List<int> weightIndices = new List<int>();
          for (int i = 0; i < vcount; i++) {
            // Make sure we convert the internal number to our bone numbers!
            jointIndices.Add(boneArrayOrder[vArray[vArrayIndex]]);
            weightIndices.Add(vArray[vArrayIndex + 1]);
            vArrayIndex += 2;
          } // for

          // If we got less than 3 values, add until we have enough,
          // this makes the computation easier below
          while (jointIndices.Count < 3)
            jointIndices.Add(0);
          while (weightIndices.Count < 3)
            weightIndices.Add(-1);

          // Find out top 3 weights
          float[] weightValues = new float[weightIndices.Count];
          int[] bestWeights = { 0, 1, 2 };
          for (int i = 0; i < weightIndices.Count; i++) {
            // Use weight of zero for invalid indices.
            if (weightIndices[i] < 0 || weightValues[i] >= weights.Length)
              weightValues[i] = 0;
            else
              weightValues[i] = weights[weightIndices[i]];

            // Got 4 or more weights? Then just select the top 3!
            if (i >= 3) {
              float lowestWeight = 1.0f;
              int lowestWeightOverride = 2;
              for (int b = 0; b < bestWeights.Length; b++)
                if (lowestWeight > weightValues[bestWeights[b]]) {
                  lowestWeight = weightValues[bestWeights[b]];
                  lowestWeightOverride = b;
                } // for if
              // Replace lowest weight
              bestWeights[lowestWeightOverride] = i;
            } // if
          } // for

          // Now build 2 vectors from the best weights
          Vector3 boneIndicesVec = new Vector3(
            jointIndices[bestWeights[0]],
            jointIndices[bestWeights[1]],
            jointIndices[bestWeights[2]]);
          Vector3 weightsVec = new Vector3(
            weightValues[bestWeights[0]],
            weightValues[bestWeights[1]],
            weightValues[bestWeights[2]]);
          // Renormalize weight, important if we got more entries before
          // and always good to do!
          float totalWeights = weightsVec.X + weightsVec.Y + weightsVec.Z;
          if (totalWeights == 0)
            weightsVec.X = 1.0f;
          else {
            weightsVec.X /= totalWeights;
            weightsVec.Y /= totalWeights;
            weightsVec.Z /= totalWeights;
          } // else

          vertexSkinJoints.Add(boneIndicesVec);
          vertexSkinWeights.Add(weightsVec);
        } // for
        #endregion
      }
*/
      #endregion

      #region Construct all triangle polygons from the vertex data
      // Construct and generate vertices lists. Every 3 vertices will
      // span one triangle polygon, but everything is optimized later.
      foreach (XmlNode trianglenode in meshNode) {
        //Fixme: make meshcontainer here prolly to fix submeshes
        if (trianglenode.Name != "triangles")
          continue;

        MeshContainer m = new MeshContainer();
        m.Name = name;


        //Fixme: check out this
        string material = XmlHelper.GetXmlAttribute(trianglenode, "material");
        string texturename = "";

        foreach (EffectNode e in EffectNodes) {
          if (e.Name == material + "-fx") {
            if ((e.InitFrom != "") && (images.ContainsKey(e.InitFrom))) {
              texturename = images[e.InitFrom];
              m.textures.Add(texturename);
            }
          } else if (e.Name == material) {
            m.materials.Add(e);
          }
        }


        // Find data source nodes
        XmlNode positionsnode = XmlHelper.GetChildNode(trianglenode, "semantic", "VERTEX");
        XmlNode normalsnode = XmlHelper.GetChildNode(trianglenode, "semantic", "NORMAL");
        XmlNode texcoordsnode = XmlHelper.GetChildNode(trianglenode, "semantic", "TEXCOORD");
        XmlNode tangentsnode = XmlHelper.GetChildNode(trianglenode, "semantic", "TEXTANGENT");

        // Get the data of the sources
        List<float> positions = sources[XmlHelper.GetXmlAttribute(positionsnode, "source").Substring(1)];
        List<float> normals = sources[XmlHelper.GetXmlAttribute(normalsnode, "source").Substring(1)];
        List<float> texcoords = sources[XmlHelper.GetXmlAttribute(texcoordsnode, "source").Substring(1)];
        //List<float> tangents = sources[XmlHelper.GetXmlAttribute(tangentsnode,"source").Substring(1)];

        // Find the Offsets
        int positionsoffset = Convert.ToInt32(XmlHelper.GetXmlAttribute(positionsnode, "offset"), NumberFormatInfo.InvariantInfo);
        int normalsoffset = Convert.ToInt32(XmlHelper.GetXmlAttribute(normalsnode, "offset"), NumberFormatInfo.InvariantInfo);
        int texcoordsoffset = Convert.ToInt32(XmlHelper.GetXmlAttribute(texcoordsnode, "offset"), NumberFormatInfo.InvariantInfo);
        //int tangentsoffset = Convert.ToInt32(XmlHelper.GetXmlAttribute(tangentsnode, "offset"), NumberFormatInfo.InvariantInfo);

        // Get the indexlist
        XmlNode p = XmlHelper.GetChildNode(trianglenode, "p");
        int[] pints = StringHelper.ConvertStringToIntArray(p.InnerText);


        int trianglecount = Convert.ToInt32(XmlHelper.GetXmlAttribute(trianglenode, "count"), NumberFormatInfo.InvariantInfo);
        m.numTriangles = trianglecount;



        // The number of ints that form one vertex:
        int vertexcomponentcount = pints.Length / trianglecount / 3;

        // Construct data
        m.vertices.Clear();
        // Initialize reuseVertexPositions and reverseReuseVertexPositions
        // to make it easier to use them below
        m.reuseVertexPositions = new int[trianglecount * 3];
        m.reverseReuseVertexPositions = new List<int>[positions.Count / 3];
        for (int i = 0; i < m.reverseReuseVertexPositions.Length; i++)
          m.reverseReuseVertexPositions[i] = new List<int>();

        // We have to use int indices here because we often have models
        // with more than 64k triangles (even if that gets optimized later).
        for (int i = 0; i < trianglecount * 3; i++) {

          // Position
          int pos = pints[i * vertexcomponentcount + positionsoffset] *3;

          Vector3 position = new Vector3(positions[pos], positions[pos + 1], positions[pos + 2]);


          //Fixme
          // Get vertex blending stuff (uses pos too)
          //Vector3 blendWeights = vertexSkinWeights[pos/3];
          //Vector3 blendIndices = vertexSkinJoints[pos/3];

          // Pre-multiply all indices with 3, this way the shader
          // code gets a little bit simpler and faster
          //blendIndices = new Vector3(blendIndices.X * 3, blendIndices.Y * 3, blendIndices.Z * 3);

          Vector3 blendWeights = new Vector3(0.0f, 0.0f, 0.0f);
          Vector3 blendIndices = new Vector3(0.0f, 0.0f, 0.0f);

          // Normal
          int nor = pints[i * vertexcomponentcount + normalsoffset] * 3;
          Vector3 normal = new Vector3(normals[nor], normals[nor + 1], normals[nor + 2]);

          // Texture Coordinates
          int tex = pints[i * vertexcomponentcount + texcoordsoffset]*2;
          float u = texcoords[tex];
          float v = 1.0f - texcoords[tex + 1]; //mirror tex coord, 

          //Fixme: tangent support was removed, not sure if we need this
          // Tangent
          //int tan = pints[i * vertexcomponentcount + tangentsoffset] * 3;
          //Vector3 tangent = new Vector3(tangents[tan], tangents[tan + 1], tangents[tan + 2]);
          Vector3 tangent = new Vector3(1, 1, 1); // dummy vector to satisfy code

          // Set the vertex
          m.vertices.Add(new SkinnedTangentVertex(position, blendWeights, blendIndices, u, v, normal, tangent));

          // Remember pos for optimizing the vertices later more easily.
          m.reuseVertexPositions[i] = pos / 3;
          m.reverseReuseVertexPositions[pos / 3].Add(i);
        }

        newmeshes.Add(m);
      }
      return newmeshes;

      #endregion
    }
    #endregion

    #region Load animation
    #region Load animation targets
    /// <summary>
    /// Animation target values to help us loading animations a little.
    /// Thanks to this dictionary we are able to load all float arrays
    /// at once and then use them wherever we need them later to fill in
    /// the animation matrices for every animated bone.
    /// </summary>
    Dictionary<string, float[]> animationTargetValues = new Dictionary<string, float[]>();

    /// <summary>
    /// Load Animation data from a collada file, ignoring timesteps,
    /// interpolation and multiple animations
    /// </summary>
    private void LoadAnimationTargets(XmlNode colladaFile) {
      try {
        // Get global frame rate
        frameRate = Convert.ToSingle(XmlHelper.GetChildNode(colladaFile, "frame_rate").InnerText);
      } catch { } // ignore if that fails

      XmlNode libraryanimation = XmlHelper.GetChildNode(colladaFile, "library_animations");
      if (libraryanimation == null)
        return;

      LoadAnimationHelper(libraryanimation);
    }

    /// <summary>
    /// Load animation helper, goes over all animations in the animationnode,
    /// calls itself recursively for sub-animation-nodes
    /// </summary>
    /// <param name="animationnode">Animationnode</param>
    private void LoadAnimationHelper(XmlNode animationnode) {
      // go over all animation elements
      foreach (XmlNode node in animationnode) {
        if (node.Name == "animation")
        //not a channel but another animation node
				{
          LoadAnimationHelper(node);
          continue;
        } // if (animation.name)

        if (node.Name != "channel")
          continue;
        string samplername =
          XmlHelper.GetXmlAttribute(node, "source").Substring(1);
        string targetname = XmlHelper.GetXmlAttribute(node, "target");

        // Find the sampler for the animation values.
        XmlNode sampler = XmlHelper.GetChildNode(animationnode, "id",
          samplername);

        // Find value xml node
        string valuename = XmlHelper.GetXmlAttribute(
          XmlHelper.GetChildNode(sampler, "semantic", "OUTPUT"),
          "source").Substring(1) + "-array";
        XmlNode valuenode = XmlHelper.GetChildNode(animationnode, "id",
          valuename);

        // Parse values and add to dictionary
        float[] values =
          StringHelper.ConvertStringToFloatArray(valuenode.InnerText);
        animationTargetValues.Add(targetname, values);

        // Set number of animation we will use in all bones.
        // Leave last animation value out later, but make filling array
        // a little easier (last matrix is just unused then).
        numOfAnimations = values.Length;
        // If these are matrix values, devide by 16!
        if (XmlHelper.GetXmlAttribute(valuenode, "id").Contains("transform"))
          numOfAnimations /= 16;
      } // foreach (node)
    } // LoadAnimationHelper(animationnode, AnimationTargetValues)
    #endregion

    #region FillBoneAnimations
    /// <summary>
    /// Fill bone animations, called from LoadAnimation after we got all
    /// animationTargetValues.
    /// </summary>
    /// <param name="colladaFile">Collada file</param>
    private void FillBoneAnimations(XmlNode colladaFile) {
      foreach (Bone bone in bones) {
        // Loads animation data from bone node sid, links them
        // automatically, also generates animation matrices.
        // Note: We only support the transform node here, "RotX", "RotY",
        // etc. will only be used if we don't got baked matrices, but that
        // is catched already when loading the initial bone matrices above.

        // Build sid the way it is used in the collada file.
        string sid = bone.id + "/" + "transform";

        int framecount = 0;
        if (animationTargetValues.ContainsKey(sid))
          // Transformation contains whole matrix (always 4x4).
          framecount = animationTargetValues[sid].Length / 16;

        // Expand array and duplicate the initial matrix in case
        // there is no animation data present (often the case).
        for (int i = 0; i < numOfAnimations; i++)
          bone.animationMatrices.Add(bone.initialMatrix);

        if (framecount > 0) {
          float[] mat = animationTargetValues[sid];
          // Load all absolute animation matrices. If you want relative
          // data here you can use the invBoneMatrix (invert initialMatrix),
          // but this won't be required here because all animations are
          // already computed. Maybe you need it when doing your own animations.
          for (int num = 0; num < bone.animationMatrices.Count &&
            num < framecount; num++) {
            bone.animationMatrices[num] = FixMatrix(LoadColladaMatrix(mat, num * 16));
          } // for (num)
        } // if (framecount)
      } // foreach (bone)
    } // FillBoneAnimations(colladaFile)
    #endregion

    #region CalculateAbsoluteBoneMatrices
    /// <summary>
    /// Calculate absolute bone matrices for finalMatrix. Not really required,
    /// but this way we can start using the bones without having to call
    /// UpdateAnimation (maybe we don't want to animate yet).
    /// </summary>
    private void CalculateAbsoluteBoneMatrices() {
      foreach (Bone bone in bones) {
        // Get absolute matrices and also use them for the initial finalMatrix
        // of each bone, which is used for rendering.
        bone.finalMatrix = bone.GetMatrixRecursively();
      } // foreach (bone)
    } // CalculateAbsoluteBoneMatrices()
    #endregion

    /// <summary>
    /// Load animation
    /// </summary>
    /// <param name="colladaFile">Collada file</param>
    private void LoadAnimation(XmlNode colladaFile) {
      // Little helper to load and store all animation values
      // before assigning them to the bones where they are used.
      LoadAnimationTargets(colladaFile);

      // Fill animation matrices in each bone (if used or not, always fill).
      FillBoneAnimations(colladaFile);

      // Calculate all absolute matrices. We only got relative ones in
      // initialMatrix for each bone right now.
      CalculateAbsoluteBoneMatrices();
    } // LoadAnimation(colladaFile)
    #endregion

    #region Update animation
    /// <summary>
    /// Was this animation data already constructed last time we called
    /// UpdateAnimation? Will not optimize much if you render several models
    /// of this type (maybe use a instance class that holds this animation
    /// data and just calls this class for rendering to optimize it further),
    /// but if you just render a single model, it gets a lot faster this way.
    /// </summary>
    private int lastAniMatrixNum = -1;

    /// <summary>
    /// Update animation. Will do nothing if animation stayed the same since
    /// last time we called this method.
    /// </summary>
    /// <param name="renderMatrix">Render matrix just for adding some
    /// offset value to the animation time, remove if you allow moving
    /// objects, this is just for testing!</param>
    private void UpdateAnimation(Matrix renderMatrix) //Fixme for animation
    {
      // Add some time to the animation depending on the position.
      //int aniMatrixNum = ((int)(100+ renderMatrix.Translation.X / 2.35f + renderMatrix.Translation.Y / 1.05f +	BaseGame.TotalTime * frameRate)) % numOfAnimations;
      //Fixme : engine time
      int aniMatrixNum = ((int)(100 * frameRate)) % numOfAnimations;
      if (aniMatrixNum < 0)
        aniMatrixNum = 0;
      // No need to update if everything stayed the same
      if (aniMatrixNum == lastAniMatrixNum)
        return;
      lastAniMatrixNum = aniMatrixNum;

      foreach (Bone bone in bones) {
        // Just assign the final matrix from the animation matrices.
        bone.finalMatrix = bone.animationMatrices[aniMatrixNum];

        // Also use parent matrix if we got one
        // This will always work because all the bones are in order.
        if (bone.parent != null)
          bone.finalMatrix *= bone.parent.finalMatrix;
      } // foreach

    } // UpdateAnimation()
    #endregion

    #region GetBoneMatrices
    /// <summary>
    /// Get bone matrices for the shader. We have to apply the invBoneSkinMatrix
    /// to each final matrix, which is the recursively created matrix from
    /// all the animation data (see UpdateAnimation).
    /// </summary>
    /// <returns></returns>
    private Matrix[] GetBoneMatrices(Matrix renderMatrix) {
      // Update the animation data in case it is not up to date anymore.
      UpdateAnimation(renderMatrix);

      // And get all bone matrices, we support max. 80 (see shader).
      Matrix[] matrices = new Matrix[System.Math.Min(80, bones.Count)];
      for (int num = 0; num < matrices.Length; num++)
        // The matrices are constructed from the invBoneSkinMatrix and
        // the finalMatrix, which holds the recursively added animation matrices
        // and finally we add the render matrix too here.
        matrices[num] = bones[num].invBoneSkinMatrix * bones[num].finalMatrix * renderMatrix;

      return matrices;
    } // GetBoneMatrices()
    #endregion

  }
}
